/*
 * Package il.ac.biu.cs.grossmm.impl.server
 * File ServiceManagerImpl.java
 * 
 * This is a part of presence service framework prototype. 
 * See javadoc for more information.
 *  
 * Copyright (C) 2006 Grossmann Mark
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.

 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

package il.ac.biu.cs.grossmm.impl.server;

import il.ac.biu.cs.grossmm.api.server.*;

import java.util.Set;


public class ServiceManagerImpl extends Node<ServiceManagerImpl> 
	implements ServiceManager {
	
	final WrappedImpl wrapped;
	final Service service;
    private boolean stopped = true;
    private ServiceListener serviceListener = null;
	

	public ServiceManagerImpl(WrappedImpl wrapped) {
		this.wrapped = wrapped;
		this.service = (Service) wrapped.getObject();
		
		Loader loader= wrapped.getLoader();
		
		if( loader instanceof ServiceListener )
			serviceListener = (ServiceListener) loader;
	}
	
	public synchronized void initialize() throws Exception {
		if( service instanceof SelfService )
			try {
				startService();
			} catch (Exception e) {
				terminated(e);
				throw e;
			}
	}
	
	public void requireService(Object serviceHint) throws Exception {
        // see if service is already required (and therefore started)
		ServiceManagerImpl peer = getByHint(serviceHint);
		if( peer != null )
			return; // already required

        // load the service
		WrappedImpl w = wrapped.load(serviceHint);
        
        // get its service manager
		peer = w.getServiceManager();
		
        // if no service manager associated, then it is not a service
		if( peer == null )
			throw new NotAServiceException(serviceHint);
		
		// TODO: start universally required services here
		
        setHint(peer, serviceHint);
        
        // TODO: removed, check conseqs.
        // w.initialize();
	}

	public synchronized void releaseService(Object serviceHint) {
		removeHint(serviceHint);
	}
	
	@Override
	public synchronized void used() throws Exception {
		startService();
	}

	@Override
	public void unused() {
		stopService(null);
		
		super.unused();
	}
    
	/**
	 * @throws Exception
	 */
	protected void startService() throws Exception
	{
		if( stopped ) {
			
			if( serviceListener != null )
				serviceListener.beforeStart(this, service);
			
			stopped = false;
			service.start(this);
			
			if( serviceListener != null )
				serviceListener.afterStart(this, service);
		}
	}
	
    protected void stopService(Exception e) {
        
        synchronized(this) {
        	if( serviceListener != null )
				serviceListener.beforeStop(this, service);
        	
            if( stopped )
                return;
            
            stopped  = true;
        }
        
        service.stop(e);
        
        if( serviceListener != null )
			serviceListener.afterStop(this, service);
        
        discard(e);
    }
	
	@Override
	protected void discarded(ServiceManagerImpl used, Set<Object> hints, Exception e) {
		try {
			for( Object hint : hints ) {
				service.terminated(hint, e);
			}
		} catch( Exception e2) {
			stopService(e2);
		}
	}
	
	@Override
	protected void onEvent(ServiceManagerImpl used, Set<Object> hints, Object event, boolean mustUnderstand) {
		if(! ( service instanceof ServerEventListener) )
			return;
		
		ServerEventListener listener = (ServerEventListener) service;
		
		for( Object hint : hints ) {
			if( !listener.onEvent(hint, event) && mustUnderstand ) {
				removeHint(hint);
				discard(new UnknownSemanticsException(event.getClass().getName()));
			}
		}
	}

	public void terminated(Exception exception) {
		discard(exception);
	}

}
